package uk.co.mmscomputing.device.twain;

import java.util.Enumeration;
import java.util.Vector;
import java.awt.image.*;
import javax.swing.*;

import java.io.*;
import java.util.Iterator;
import javax.imageio.*;
import javax.imageio.stream.*;

import uk.co.mmscomputing.device.scanner.*;

public class TwainScanner extends Scanner implements TwainConstants {

	public TwainScanner() {
		metadata = new TwainIOMetadata();
		// jtwain.setScanner(this);
	}

	// call native routine; TwainScanner -> jtwain.class -> jtwain.dll

	public void select() throws ScannerIOException { // select twain source
		jtwain.select(this);
	}

	public TwainIdentity[] getIdentities(){
    Vector<TwainIdentity> identities=new Vector<TwainIdentity>();
    try{
      jtwain.getIdentities(this,identities);
    }catch(Exception e){
      metadata.setException(e);
      fireListenerUpdate(metadata.EXCEPTION);
    }
    return identities.toArray(new TwainIdentity[identities.size()]);
  }

	public String[] getDeviceNames() throws ScannerIOException {
		Vector identities = new Vector();

		jtwain.getIdentities(this, identities);

		String[] names = new String[identities.size()];
		Enumeration ids = identities.elements();
		for (int i = 0; ids.hasMoreElements(); i++) {
			TwainIdentity id = (TwainIdentity) ids.nextElement();
			names[i] = id.getProductName();
		}
		return names;
	}

	public void select(String name) throws ScannerIOException { // select twain source
		jtwain.select(this, name);
	}

	public String getSelectedDeviceName() throws ScannerIOException {
		return jtwain.getSource().getProductName();
	}

	public void acquire() throws ScannerIOException { // initiate scan
		jtwain.acquire(this);
	}

	public void setCancel(boolean c) throws ScannerIOException {
		jtwain.setCancel(this, c);
	}

	// callback function jtwain.dll -> jtwain.class -> TwainScanner.class

	void setImage(BufferedImage image) { // received an image
		try {
			metadata.setImage(image);
			fireListenerUpdate(metadata.ACQUIRED);
		} catch (Exception e) {
			metadata.setException(e);
			fireListenerUpdate(metadata.EXCEPTION);
		}
	}

	void setImage(File file) { // received an image using file transfer
		try {
			metadata.setFile(file);
			fireListenerUpdate(metadata.FILE);
		} catch (Exception e) {
			metadata.setException(e);
			fireListenerUpdate(metadata.EXCEPTION);
		}
	}

	void setImageBuffer(TwainTransfer.MemoryTransfer.Info info) {
		// System.out.println(info.toString());

		try {
			((TwainIOMetadata) metadata).setMemory(info);
			fireListenerUpdate(metadata.MEMORY);
		} catch (Exception e) {
			metadata.setException(e);
			fireListenerUpdate(metadata.EXCEPTION);
		}
	}

	protected void negotiateCapabilities(TwainSource source) {
		// Called in jtwain when source is in state 4: negotiate capabilities
		((TwainIOMetadata) metadata).setSource(source);
		fireListenerUpdate(metadata.NEGOTIATE);
		if (metadata.getCancel()) { // application wants us to abort scan
			try {
				source.close();
			} catch (Exception e) {
				metadata.setException(e);
				fireListenerUpdate(metadata.EXCEPTION);
			}
		}
	}

	void setState(TwainSource source) {
		metadata.setState(source.getState());
		((TwainIOMetadata) metadata).setSource(source);
		fireListenerUpdate(metadata.STATECHANGE);
	}

	void signalInfo(String msg, int val) {
		metadata.setInfo(msg + " [0x" + Integer.toHexString(val) + "]");
		fireListenerUpdate(metadata.INFO);
	}

	void signalException(String msg) {
		Exception e = new TwainIOException(getClass().getName() + ".setException:\n    " + msg);
		metadata.setException(e);
		fireListenerUpdate(metadata.EXCEPTION);
	}

	public boolean isAPIInstalled() {
		return jtwain.isInstalled();
	}

	public JComponent getScanGUI() throws ScannerIOException {
		return getScanGUI(4);
	}

	public JComponent getScanGUI(int mode) throws ScannerIOException {
		return new TwainPanel(this, mode);
	}

	// protected void finalize()throws Throwable{
	// System.err.println(getClass().getName()+".finalize:");
	// }

	static public Scanner getDevice() {
		return new TwainScanner();
	}
}
